// Using the MatlabControlService example:
// -------------------------------
//await ExecuteCommandsAsync("a = 1;");
//await ExecuteCommandsAsync("b = \"aaa\";");
//await ExecuteCommandsAsync(@"c = ""woshizifuchuan"";");
//string variableValuesInfo = GetMatlabVariable();
//string variableValueC = GetMatlabVariable("c");
//string res1 = ExecuteMatlabCommand("d = 12", false);
//string res2 = ExecuteMatlabCommand("num2str(d)", true);
using MathWorks.MATLAB.Engine;
using MathWorks.MATLAB.Types;
using MultiAgentsShared;

namespace MultiAgentsClient.Tools;

[GptToolSet("Matlab Control ToolSet", "")]
public class MatlabControlToolProvider
{
    // The matlab engine instance.
    private static dynamic? _matlabEng;


    //[GptFunc("Gathers and returns information about all variables currently in the MATLAB workspace using the MATLAB Engine API. " +
    //"The function retrieves details such as the name, size, bytes, class, and complexity of each variable in the workspace. " +
    //"The information for all variables is encoded as a JSON string, providing a structured overview of the workspace's content. " +
    //"This makes it easy to programmatically inspect the workspace variables for further processing or analysis. " +
    //"In case of an execution error, a descriptive error message is returned, indicating the nature of the problem encountered.",
    //    typeof(ClientToolSetConfig))]
    public static string GetMatlabVariablesInfo()
    {
        try
        {
            _matlabEng.eval(new RunOptions(nargout: 0), $@"
tempVars = whos;
tempStrVars = jsonencode(tempVars);");
            string stringVarValue = _matlabEng.eval(new RunOptions(nargout: 1), "tempStrVars");
            _matlabEng.eval(new RunOptions(nargout: 0), @"clear tempVars tempStrVars;");

            return stringVarValue;
        }
        catch (Exception ex)
        {
            Debug.LogLine($"An error occurred while getting variable info: {ex.Message}");

            return $"An error occurred while getting variable info: {ex.Message}";
        }
    }

    //[GptFunc("Retrieves the value of a specified variable from the MATLAB workspace using the MATLAB Engine API. " +
    //"The function supports various types of variables including numeric, logical, character, string, cell arrays, and structures. " +
    //"For numeric and logical types, the value is returned as a MATLAB string representation. " +
    //"For character arrays and strings, the direct string value is returned. " +
    //"For cell arrays and structures, the value is returned as a JSON-encoded string. " +
    //"If the specified variable is of an unsupported type or does not exist in the workspace, " +
    //"a relevant error message is returned indicating either the unsupported type or non-existence of the variable.",
    //    typeof(ClientToolSetConfig))]
    public static string GetMatlabVariableValue(
    [GptFuncParam("the name of the variable whose value is to be retrieved from the MATLAB workspace. " +
    "The function checks if this variable exists in the current workspace and attempts to return its value as a string.")]
    string variableName)
    {
        try
        {
            _matlabEng.eval(new RunOptions(nargout: 0), $@"
tempVarNames = who;
tempVarName = '{variableName}';
if ismember(tempVarName, tempVarNames)
    tempValue = eval(tempVarName);
    if isnumeric(tempValue), tempStrValue = mat2str(tempValue);
    elseif islogical(tempValue), tempStrValue = mat2str(tempValue);
    elseif ischar(tempValue) || isstring(tempValue), tempStrValue = string(tempValue);
    elseif iscell(tempValue) || isstruct(tempValue), tempStrValue = jsonencode(tempValue);
    else, tempStrValue = ""Unsupported type"";
    end
else
    tempStrValue = ""Variable '"" + tempVarName + ""' does not exist."";
end");
            string stringVarValue = _matlabEng.eval(new RunOptions(nargout: 1), "tempStrValue");
            _matlabEng.eval(new RunOptions(nargout: 0), @"clear tempVarNames tempVarName tempValue tempStrValue;");

            return stringVarValue;
        }
        catch (Exception ex)
        {
            Debug.LogLine($"An error occurred while getting variable '{variableName}': {ex.Message}");

            return $"An error occurred while getting variable '{variableName}': {ex.Message}";
        }
    }

    [GptFunc("MATLAB Command Execution:" +
            "This function can execute any MATLAB command, supporting both types that produce a return value and those that do not." +
            "Commands requiring a return must deliver a result as a string, since only string outputs are supported." +
            "Successful command execution returns the MATLAB output as a string if expected, or an empty string if not." +
            "Errors during execution will return a detailed error message, highlighting the cause of the failure.", true)]
    public static async Task<string> ExecuteMatlabCommand(
    [GptFuncParam("the MATLAB command to be executed")]
    string command,
    [GptFuncParam("indicates whether the command is expected to return a value. If true, the command must return a string value. No other return types are supported.")]
    bool hasReturnValue)
    {
        var startMatlabResult = await GetOrStartMatlabAsync();

        if (startMatlabResult != true.ToString())
        {
            return startMatlabResult;
        }
        else
        {
            try
            {
                if (hasReturnValue)
                {
                    string result = _matlabEng.eval(new RunOptions(nargout: 1), command);
                    return result;
                }
                else
                {
                    _matlabEng.eval(new RunOptions(nargout: 0), command);
                    return "";
                }
            }
            catch (Exception ex)
            {
                return $"An error occurred while executing MATLAB command '{command}': {ex.Message}";
            }
        }
    }

    private static async Task<string> GetOrStartMatlabAsync()
    {
        if (_matlabEng != null)
        {
            return true.ToString();
        }

        try
        {
            var existingMatlabSessions = await MATLABEngine.FindMATLABAsync();
            if (existingMatlabSessions.Length == 0)
            {
                var startupOption = new string[] { "-desktop" };
                _matlabEng = await MATLABEngine.StartMATLABAsync(startupOption);
                if (_matlabEng == null)
                {
                    return "Failed to start MATLAB async, with no exceptions thrown";
                }

                //matlab.engine.shareEngine
                return true.ToString();
            }
            else
            {
                string existingMatlabSession = existingMatlabSessions[0]; // Pick the first

                _matlabEng = await MATLABEngine.ConnectMATLABAsync(existingMatlabSession);
                if (_matlabEng == null)
                {
                    return "Failed to connect to shared session async, with no exception thrown";
                }

                return true.ToString();
            }

            //var prjPath = "D:\\MMWRelated\\PostAnalysis\\PostAnalysis.prj";
            //_matlabEng.open(prjPath);
            //MatlabStartupDone?.Invoke();
        }
        catch (Exception e)
        {
            return e.Message;
        }

    }

}
